import { Service } from 'egg';
import * as blue from 'bluebird';
import * as _ from 'lodash';
import * as Excel from 'exceljs';
import { ReadPreference } from 'mongodb';
const DB_ACCOUNT_PRODUCT = 'platform_account_product';
// const DB_ACCOUNT = 'platform_account';
const DB_CALL = '_c5_call_sheet';

let time1;

type CALL_DOC = {
  _id: string;
  OFFERING_TIME: string;
  CALL_TIME_LENGTH: number;
  CONNECT_TYPE: string;
  STATUS: string;
  datadb: string;
  account: string;
}

interface ReportType {
  account?: string;
  reportCalloutCount: any;
  reportCallInCount: any;
  reportCallOutLen: any;
  reportCallInLen: any;
  reportCallInAccessCount: any;
  reportCallOutAccessCount: any;
}

// const allAns: ReportType[] = [];

export default class SjCallService extends Service {
  
  /**
   * 计算数据给李蔓，2020年11月12日12:00:12
   */
  public async tjcall() {
    time1 = new Date().getTime();

    const body = this.ctx.request.query;
    const {startTime, endTime} = body;
    const isoffline = body.isoffline;

    const concurrency = parseInt(body.concurrency) || 1;

    this.ctx.logger.info('concurrency_log: ', concurrency);

    const query = {
      OFFERING_TIME: {
        $gte: startTime || '2017-01-01 00:00:00',
        $lte: endTime || '2020-10-01 00:00:00',
      },
      CONNECT_TYPE: {$in: ['dialout', 'normal']},
      // CONNECT_TYPE: 'dialout',
      // STATUS: 'dealing',
    };

    const accounts = await this.getAccounts();
    const all = accounts.length;

    let current = 0;
    
    await blue.map(accounts, async(accountDoc) => {
      const accountId = accountDoc.account;
      const datadb = accountDoc.dataDB;
      
      const reportCalloutCount = {};
      const reportCallOutAccessCount = {};
      const reportCallInCount = {};
      const reportCallInAccessCount = {};
      const reportCallOutLen = {};
      const reportCallInLen = {};
      const reportAll = {
        reportCalloutCount,
        reportCallInCount,
        reportCallOutLen,
        reportCallInLen,
        reportCallOutAccessCount,
        reportCallInAccessCount,
      }

      // this.app.logger.info('do_coun=' + accountId );

      let oneAll;
      oneAll = await this.app.commonDB.db(datadb).collection(`${accountId}${DB_CALL}`).count();
      
      this.app.logger.info('do_count_res==' + accountId + ' ' + oneAll);
      if (oneAll === 0) {
        current++;
        return;
      }
      let oneCurrent = 0;

      let cursor;
      if (isoffline) {
        this.app.logger.info('offline_yes', accountId);
        cursor = await this.app.commonDB.db(datadb).collection(`${accountId}${DB_CALL}`).find(query, {fields: {
          OFFERING_TIME: 1,
          CALL_TIME_LENGTH: 1,
          CONNECT_TYPE: 1,
          STATUS: 1,
          _id: 0,
        }}).setReadPreference(new ReadPreference('secondary',[{'offline': 'true'}])).batchSize(10000);
      } else {
        this.app.logger.info('offline_no', accountId);
        cursor = await this.app.commonDB.db(datadb).collection(`${accountId}${DB_CALL}`).find(query, {fields: {
          OFFERING_TIME: 1,
          CALL_TIME_LENGTH: 1,
          CONNECT_TYPE: 1,
          STATUS: 1,
        }}).setReadPreference(ReadPreference.SECONDARY_PREFERRED).batchSize(10000);
      };
      
      let doc: CALL_DOC;
      let hasDoc = false;
      while (await cursor.hasNext()) {
        hasDoc = true;
        doc = await cursor.next();
        doc.datadb = datadb;
        doc.account = accountId;
        this.processOneCall(doc, reportAll);
        oneCurrent++;
        this.printOneRate(oneAll, oneCurrent, accountId, datadb, doc);
      }
      if (hasDoc) {
        await this.processOneAccountEnd(accountId, reportAll);
      }
      current++;
      this.printRate(all, current);

    }, {concurrency});

    await this.sumAllAccount();
    this.ctx.sendSuccess();
  }

  public clearReportInMemory(reportAll: ReportType) {
    reportAll.reportCallInAccessCount= {};
    reportAll.reportCallInCount = {};
    reportAll.reportCallInLen = {};
    reportAll.reportCallOutAccessCount = {};
    reportAll.reportCallOutLen = {};
    reportAll.reportCalloutCount = {};
  }

  public async printRate(all: number, current: number) {
    if (current % 1 === 0) {
      this.app.logger.info('======account==rate======' + `${all} ${current} ${Math.round(current/all*100)}%`);
    }
  }

  public async printOneRate(all: number, current: number, accountId: string, datadb: string, doc: CALL_DOC) {
    if (current % 50000 === 0) {
      this.app.logger.info(`======${accountId}=${datadb}===rate==${doc.OFFERING_TIME} ${all} ${current} ${Math.round(current/all*100)}%`);
    }
  }

  public async processOneAccountEnd(accountId: string, reportall: ReportType) {
    await this.app.commonDB.db('cc').collection('call_month_tj')
      .findOneAndUpdate({_id: accountId}, {$set: { account: accountId, data: reportall}}, {upsert: true});
  }

  public processOneCall(doc: CALL_DOC, reportall: ReportType) {
    const {reportCallInCount, reportCalloutCount, reportCallInLen, reportCallOutLen, reportCallInAccessCount, reportCallOutAccessCount} = reportall;
    const month = this.getMonthFromTime(doc.OFFERING_TIME);
    reportCallInCount[month] = reportCallInCount[month] || 0;
    reportCallInAccessCount[month] = reportCallInCount[month] || 0;
    reportCallOutAccessCount[month] = reportCallInCount[month] || 0;
    reportCalloutCount[month] = reportCalloutCount[month] || 0;
    reportCallInLen[month] = reportCallInLen[month] || 0;
    reportCallOutLen[month] = reportCallOutLen[month] || 0;
    if (doc.CONNECT_TYPE === 'normal') {
      reportCallInCount[month]++;
      if (doc.STATUS === 'dealing') {
        reportCallInAccessCount[month]++;
        let len = 0;
        try {
          len = parseInt(doc.CALL_TIME_LENGTH + '');
        } catch (error) {
        }
        reportCallInLen[month] += len;
      }
    } else if (doc.CONNECT_TYPE === 'dialout') {
      reportCalloutCount[month]++;
      if (doc.STATUS === 'dealing') {
        reportCallOutAccessCount[month]++;
        let len = 0;
        try {
          len = parseInt(doc.CALL_TIME_LENGTH + '');
        } catch (error) {
        }
        reportCallOutLen[month] += len;
      }
    }
  }

  // "2020-08-18 16:02:26" => "202008"
  public getMonthFromTime(time: string): string {
    return time.substr(0, 4) + time.substr(5, 2);
  }

  // "2020-08-18 16:02:26" => "16"
  public getHourFromTime(time: string): string {
    return time.substr(11,2);
  }

  public async sumAllAccount() {
    this.ctx.logger.info('开始统计总数：');
    const sum: ReportType = {
      reportCalloutCount: 0,
      reportCallInCount: 0,
      reportCallOutLen: 0,
      reportCallInLen: 0,
      reportCallOutAccessCount: 0,
      reportCallInAccessCount: 0,
    };
    
    const list = await this.app.commonDB.db('cc').collection('call_month_tj').find().toArray();
    list.forEach(one => {
      one = one.data;
      const rData: ReportType = one.data;
      Object.keys(rData.reportCallInCount).forEach((month) => {
        sum.reportCallInCount[month] = sum.reportCallInCount[month] || 0;
        sum.reportCallInLen[month] = sum.reportCallInLen[month] || 0;
        sum.reportCallInAccessCount[month] = sum.reportCallInAccessCount[month] || 0;
        sum.reportCallInCount[month] += (rData.reportCallInCount || 0);
        sum.reportCallInAccessCount[month] += (rData.reportCallInAccessCount || 0);
        sum.reportCallInLen[month] += (rData.reportCallInLen || 0);
      });
      Object.keys(rData.reportCalloutCount).forEach((month) => {
        sum.reportCalloutCount[month] = sum.reportCalloutCount[month] || 0;
        sum.reportCallOutAccessCount[month] = sum.reportCallOutAccessCount[month] || 0;
        sum.reportCallOutLen[month] = sum.reportCallOutLen[month] || 0;
        sum.reportCalloutCount[month] += (rData.reportCalloutCount || 0);
        sum.reportCallOutAccessCount[month] += (rData.reportCallOutAccessCount || 0);
        sum.reportCallOutLen[month] += (rData.reportCallOutLen || 0);
      });
    });
    this.processAllEnd(sum);
  }

  public async processAllEnd(all: ReportType) {
    // const {reportHour, reportMonth, reportCon, reportLen} = reportall;
    const filename = `./logs/call_tj-${Date.now()}.xlsx`;
    const options = {
      filename,
      useStyles: true,
      useSharedStrings: true
    };
    const workbook = new Excel.stream.xlsx.WorkbookWriter(options);
    // sheet1
    const sheet = workbook.addWorksheet('通话量月份统计');
    sheet.columns = [
      { key: 'month', header: '月份' } as any,
      { key: 'reportCallInCount', header: '呼入总数' } as any,
      { key: 'reportCallInAccessCount', header: '呼入接通数' } as any,
      { key: 'reportCallInLen', header: '呼入时长' } as any,
      { key: 'reportCalloutCount', header: '外呼总数' } as any,
      { key: 'reportCallOutAccessCount', header: '外呼接通数' } as any,
      { key: 'reportCallOutLen', header: '外呼总时长' } as any,
    ];
    const timeList = ['201701', '201702', '201703', '201704', '201705', '201706', '201707', '201708', '201709', '201710', '201711', '201712', 
    '201801', '201802', '201803', '201804', '201805', '201806', '201807', '201808', '201809', '201810', '201811', '201812', 
    '201901', '201902', '201903', '201904', '201905', '201906', '201907', '201908', '201909', '201910', '201911', '201912', 
    '202001', '202002', '202003', '202004', '202005', '202006', '202007', '202008', '202009', '202010',
    ];
    timeList.forEach((month) => {
      const obj = {
        reportCallInCount: all.reportCallInCount[month],
        reportCallInAccessCount: all.reportCallInAccessCount[month],
        reportCallInLen: all.reportCallInLen[month],
        reportCalloutCount: all.reportCalloutCount[month],
        reportCallOutAccessCount: all.reportCallOutAccessCount[month],
        reportCallOutLen: all.reportCallOutLen[month],
      }
      sheet.addRow(obj).commit();;
    });
    sheet.commit();
    console.time('执行commit');
    await workbook.commit();
    console.timeEnd('执行commit');
    const time2 = new Date().getTime();
    this.ctx.logger.info(`导出耗时：${time2 - time1} ms`);
  }


  public async getAccounts(): Promise<{dataDB: string, account:string}[]> {
    const accounts = await this.app.commonDB.db('bill').collection(DB_ACCOUNT_PRODUCT)
      .find({product: 'cc', createTime: {$lt: '2020-09-01 00:00:00'}}, {fields: {_id: 1, dataDB:1, account:1}}).toArray();
    this.ctx.logger.info(accounts.length);
    return accounts as any;
  }

  // public async getDataDB(account) {
  //   let db = dbCache[account];
  //   if (!db) {
  //     const doc = await this.app.commonDB.db('bill').collection(DB_ACCOUNT_PRODUCT)
  //       .findOne({_id: account + '_cc'}, {fields: {dataDB:1}});
  //     if (!doc) {
  //       return null;
  //     }
  //     dbCache[account] = doc.dataDB;
  //     db = doc.dataDB;
  //   }
  //   return db;
  // }

}
